- Published on
Github Action CI/CD
- Authors
- Name
- Prover
Github Action 实现 CI/CD
服务技术栈:JDK17 + Springboot3 + Maven + Docker
环境准备:
- 服务器上安装下 docker, nginx 然后 ssh 打开即可
- runner 的话可以用 github 官方的(每个月都 2000 分钟的限制),也可以用自己电脑当作的 runner(无限制)
说明:
- 主要是通过 github action 实现镜像的构建和上传
- 部署主要是通过远程执行服务器脚本,然后传入参数,拉取指定镜像并运行容器 + 重启 nginx 服务器
- 这里的话主要是通过容器暴漏指定端口,然后把 nginx 对应的 url 前缀映射到容器上
实现步骤:
准备一个镜像仓库:我这里用的阿里云 acr
创建一个个人版实例
创建一个镜像仓库,可以和服务名保持一致
创建一个命名空间
在访问凭证中设置固定密码
最后要上传的仓库地址可以打开镜像仓库看到
[可选] 准备 runner 的环境,因为我这里是 windows 所以单独说一下
安装 windows 版的 docker 服务
安装 windows ssh 客户端服务, 打开 管理员PowerShell
查看是否安装了
Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'
这里只需要安装 ssh 客户端
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
要在 windows 上生成 ssh 密钥,然后把公钥拷贝到服务上,注意不要重名把服务器的公钥覆盖了
然后执行两个命令把公钥加到
authorizad_keys
中cat 你上传的公钥名.pub >> authorized_keys echo -e "#" >> authorized_keys
准备 runner 环境,这个很简单:打开 Setting -> Actions -> Runners -> New self-hosted runner 按照其中的步骤即可添加自己的 runner
将 docker 仓库的信息保存到 github 仓库的 Action secrets 中,这样就可以在工作流脚本中使用
在远程服务器上编写适合自己项目的部署脚本,我这里放在了
/usr/local/deplpy.sh
# 拉取镜像并启动容器 docker pull $IMAGE docker stop $PROJECT_NAME || true docker rm $PROJECT_NAME || true docker run -d --name $PROJECT_NAME -p $APP_PORT:$APP_PORT $IMAGE docker rmi $IMAGE || true # 动态生成 Nginx 配置 # NGINX_TEMPLATE="/usr/local/nginx/conf.d/project.conf.template" NGINX_MAIN_CONF="/usr/local/nginx/conf/nginx.conf" TMP_CONF="/tmp/nginx_tmp.conf" LOCK_FILE="/tmp/nginx_conf.lock" ESCAPED_PREFIX=$(echo "$URL_PREFIX" | sed 's/\//\\\//g') ( flock -x -w 30 200 || { echo "[ERROR] 获取文件锁超时"; exit 1; } # 转义 URL 前缀中的特殊字符(/ { } 等) ESCAPED_PREFIX=$(echo "$2" | sed 's/\//\\\//g; s/{/\\{/g; s/}/\\}/g') # 步骤1:生成临时文件并删除旧配置(如果存在) if grep -q "location $ESCAPED_PREFIX {" $NGINX_MAIN_CONF; then sed "/location $ESCAPED_PREFIX {/,/}/d" $NGINX_MAIN_CONF > $TMP_CONF else cp $NGINX_MAIN_CONF $TMP_CONF fi # 步骤2:插入新配置到 server 块内 sed -i "/server {/a\\ location $2 {\\ proxy_pass http://localhost:$3;\\ proxy_set_header Host \$host;\\ proxy_set_header X-Real-IP \$remote_addr;\\ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;\\ }" $TMP_CONF # 步骤3:检查临时文件有效性 if [ ! -s $TMP_CONF ] || ! grep -q "server {" $TMP_CONF; then echo "[ERROR] 临时配置文件无效,拒绝覆盖原文件!" exit 1 fi # 步骤4:替换原配置 mv $TMP_CONF $NGINX_MAIN_CONF # 4. 验证并重载 Nginx /usr/local/nginx/sbin/nginx -t && /usr/local/nginx/sbin/nginx -s reload ) 200>$LOCK_FILE
编写 github 工作流脚本,在项目的根目录下创建一个
.github/workflows/docker-build-deploy.yml
name: Docker Build, Push & Deploy on: push: branches: [ "main" ] # 触发分支(按需修改) workflow_dispatch: # 允许手动触发 env: DOCKER_REGISTRY: 你的镜像仓库地址 IMAGE_NAME: 镜像名称 TAG: ${{ github.sha }} IMAGE_NAME_SPACE: 命名空间 URL_PREFIX: 服务路径 APP_PORT: 服务端口 jobs: build-and-push: runs-on: self-hosted steps: - name: Checkout Code uses: actions/checkout@v4 # 使用 setup-java 内置缓存功能 - name: Set up JDK with Caching uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' cache: maven - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker Registry uses: docker/login-action@v2 with: registry: ${{ env.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push Docker Image uses: docker/build-push-action@v4 with: network: host context: . file: ./Dockerfile push: true tags: | ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME_SPACE }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME_SPACE }}/${{ env.IMAGE_NAME }}:latest cache-from: type=gha,timeout=120m cache-to: type=gha,mode=max,timeout=120m - name: SSH Deploy run: | ssh root@你的服务器地址 -i id_ed25519 " /usr/local/deploy.sh \ ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME_SPACE }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} \ ${{ env.URL_PREFIX }} \ ${{ env.APP_PORT }} \ ${{ env.IMAGE_NAME }} "
- 注意:如果你的 runner 环境是 Linux 服务器,那么最后的 ssh 部署的命令不太一样,可以用
appleboy/ssh-action@v1
插件实现
- 注意:如果你的 runner 环境是 Linux 服务器,那么最后的 ssh 部署的命令不太一样,可以用
给项目编写一个
Dockerfile
# 第一阶段:使用 Maven 构建 JAR(构建环境) FROM maven:3.9-eclipse-temurin-17-alpine AS builder # 设置工作目录 WORKDIR /app # 复制 POM 和源码(利用 Docker 层缓存优化) COPY pom.xml . RUN mvn dependency:go-offline -DskipTests COPY src ./src RUN mvn clean package -DskipTests # 第二阶段:运行 JAR(生产环境) FROM eclipse-temurin:17-jre-alpine # 设置时区(可选) ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 设置工作目录 WORKDIR /app # 从构建阶段复制 JAR 文件 COPY /app/target/*.jar app.jar # 暴露端口(与 Spring Boot 配置的端口一致) EXPOSE 8080 # 启动应用(优化 JVM 参数) ENTRYPOINT ["java", "-jar", "app.jar"]
然后提交代码到 main 分支上即可运行啦
遗留问题:
- github docker build 每隔一段时间都要重新下载其中的 maven 依赖,无语了,不知道怎么搞
- 部署脚本还有点问题,如果有注释的
server
配置,加的时候会报错 - 一些 Dockerfile 的参数可以放到环境变量中